package com.rzjm.api.netty.water;

import cn.hutool.json.JSONUtil;
import com.rzjm.api.entity.DeviceWater;
import com.rzjm.api.entity.DeviceWaterAnswerMessages;
import com.rzjm.api.netty.water.constant.ResultCodeEnum;
import com.rzjm.api.netty.water.constant.SendRtEnum;
import com.rzjm.api.netty.water.entity.SendData;
import com.rzjm.api.netty.water.entity.SendObjectData;
import com.rzjm.api.service.IDeviceWaterAnswerMessagesService;
import com.rzjm.api.service.IDeviceWaterService;
import com.rzjm.api.util.R;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Optional;

/**
 * @author WangWT
 * @Description:
 * @date 2023/3/31 10:33
 */
@Component
public class SendUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(SendUtil.class);

    private static IDeviceWaterAnswerMessagesService answerMessagesService;
    private static IDeviceWaterService devicesService;

    @Autowired
    public void setAnswerMessagesService(IDeviceWaterAnswerMessagesService answerMessagesService) {
        SendUtil.answerMessagesService = answerMessagesService;
    }
    @Autowired
    public void setDevicesService(IDeviceWaterService devicesService) {
        SendUtil.devicesService = devicesService;
    }


    /**
     * 向设备下发指令入口
     *
     * @param devices  设备
     * @param sendData 下发消息
     * @param isBlock  true-阻塞等回，false-不等回。  是否阻塞等待回复，仅firmwareVersion > 134时生效，批量下发操作建议不等回
     * @return
     */
    public static R<Object> sentCommand(DeviceWater devices, SendData sendData, boolean isBlock) {
        sendData = packageData(sendData);
        if (sendData == null) {
            return R.failed("要下发的数据包存在异常");
        }
		ResponseData res;
		if (isBlock) {
			res = sentSyncCommand(devices.getId(), sendData, 100 * 1000);
		} else {
			res = sentSyncNonBlockCommand(devices.getId(), sendData);
		}
        if (res.getCode() == ResultCodeEnum.OK.getCode()) {
            LOGGER.info("向设备下发指令成功，deviceId={},  isBlock ={}, code={},msg={},data = {}"
                , devices.getId(), isBlock, res.getCode(), res.getMsg(), JSONUtil.toJsonStr(sendData));
            return R.ok();
        } else {
            LOGGER.error("向设备下发指令失败，deviceId={},  isBlock ={}, code={},msg={},data = {}"
                , devices.getId(), isBlock, res.getCode(), res.getMsg(), JSONUtil.toJsonStr(sendData));
            return R.failed(res.getMsg());
        }
    }


    /**
     * 异步发送
     *
     * @param deviceId
     * @param sendData
     * @return
     */
    private static ResponseData sentAsyncCommand(int deviceId, SendData sendData) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message = JSONUtil.toJsonStr(sendData);
        return CommandSender.sentAsyncCommand(deviceId, message);
    }

    /**
     * 同步发送
     *
     * @param deviceId
     * @param sendData
     * @param timeout  超时时间，单位毫秒
     * @return
     */
    private static ResponseData sentSyncCommand(int deviceId, SendData sendData, int timeout) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message;
        if (sendData.getTag() == null) {
            long tag = getTag(deviceId, sendData.getRt());
            sendData.setTag(tag);
            message = JSONUtil.toJsonStr(sendData);
            saveMsg(deviceId, tag, rt, message);
        } else {
            message = JSONUtil.toJsonStr(sendData);
        }
        return CommandSender.sentSyncCommand(deviceId, sendData.getTag(), message, timeout);
    }

    /**
     * 同步非阻塞发送
     * 只记录消息日志，不会阻塞等待消息回复，相当于异步，但还是会在消息接收处，将日志状态改成已回复。
     *
     * @param deviceId
     * @param sendData
     * @return
     */
    private static ResponseData sentSyncNonBlockCommand(int deviceId, SendData sendData) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message;
        if (sendData.getTag() == null) {
            long tag = getTag(deviceId, sendData.getRt());
            sendData.setTag(tag);
            message = JSONUtil.toJsonStr(sendData);
            saveMsg(deviceId, tag, rt, message);
        }else {
            message = JSONUtil.toJsonStr(sendData);
        }
        return CommandSender.sentAsyncCommand(deviceId, message);
    }

    /**
     * 存储消息日志
     *
     * @param deviceId
     * @param tag
     * @param rt
     * @param message
     */
    private static void saveMsg(int deviceId, long tag, int rt, String message) {
        try {
            //同步方法保存消息
            DeviceWaterAnswerMessages answerMessages = new DeviceWaterAnswerMessages();
            answerMessages.setDeviceId(deviceId);
            answerMessages.setTag(tag);
            answerMessages.setRt(rt);
            answerMessages.setData(message);
            Date date = new Date();
            answerMessages.setCreatedAt(date);
            answerMessages.setUpdatedAt(date);
//            try {
//                LoginUser loginUser = LoginHelper.getLoginUser();
//                answerMessages.setUserName(loginUser.getUsername());
//                answerMessages.setUserType(getUserType());
//                answerMessages.setUserId(loginUser.getUserId());
//            } catch (Exception e) {
//                answerMessages.setUserName("web端用户");
//                answerMessages.setUserType(0);
//                answerMessages.setUserId(null);
//            }
            answerMessages.setDataDescription(getDesc(message));
            Optional.ofNullable(answerMessages.getDeviceId())
                .map(devicesService::findById)
                .ifPresent(device -> {
                    answerMessages.setImei(device.getImei());
                });
            answerMessagesService.save(answerMessages);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 汉化指令
     * @param data
     * @return
     */
    private static String getDesc(String data){
        SendObjectData sendData = JSONUtil.toBean(data, SendObjectData.class);
        return sendData.toString();
    }

    private static long getTag(int deviceId, Integer rt) {
        int random = (int)(Math.random() * 9000000 + deviceId);
        String rtStr = deviceId + "" + random;//String.valueOf(new Date().getTime());// + String.valueOf(rt);
//        if (rt < 10) {
//            rtStr = "0" + rtStr;
//        }
        return Long.parseLong(rtStr);
    }

    /**
     * 发送前数据再次封装处理
     *
     * @param sendData
     * @return
     */
    private static SendData packageData(SendData sendData) {
        if (sendData == null) {
            LOGGER.error("要下发的数据包不能为空");
            return null;
        }
        Integer rt = sendData.getRt();
        if (rt == null) {
            LOGGER.error("要下发的数据必须包含rt");
            return null;
        }
        SendRtEnum sendRt = SendRtEnum.getByRt(rt);
        if (sendRt == null) {
            LOGGER.error("设备请求的数据rt不支持，rt：{}", rt);
            return null;
        }
        switch (sendRt) {
            case SEND_RT_1:
            case SEND_RT_2:
            case SEND_RT_3:
            case SEND_RT_4:
            case SEND_RT_5:
            case SEND_RT_6:
            case SEND_RT_7:
            case SEND_RT_8:
            case SEND_RT_9:
            case SEND_RT_10:
            case SEND_RT_11:
            default:
//                SendData sendData99 = new SendData();
//                sendData99.setRt(SEND_RT_99.getRt());
//                return sendData99;
        }
        return sendData;
    }



    ////////////////////////////////////////////////////////////////////////


    /**
     * 向设备下发指令入口
     *
     * @param devices  设备
     * @param sendData 下发消息
     * @param isBlock  true-阻塞等回，false-不等回。  是否阻塞等待回复，仅firmwareVersion > 134时生效，批量下发操作建议不等回
     * @return
     */
    public static R<Object> sentCommand(DeviceWater devices, SendObjectData sendData, boolean isBlock) {
        if (sendData == null) {
            return R.failed("要下发的数据包存在异常");
        }
		ResponseData res;
//		int firmwareVersion = Optional.ofNullable(devices.getFirmwareVersion()).map(Integer::valueOf).orElse(0);
//        //版本大于134时命令需要带有tag，tag代表消息标识
//        if (firmwareVersion > 0) {
//            if (isBlock) {
//                res = sentSyncCommand(devices.getId(), sendData, 20 * 1000);
//            } else {
//                res = sentSyncNonBlockCommand(devices.getId(), sendData);
//            }
//        } else {
//            res = sentAsyncCommand(devices.getId(), sendData);
//        }
		if (isBlock) {
			res = sentSyncCommand(devices.getId(), sendData, 20 * 1000);
		} else {
			res = sentSyncNonBlockCommand(devices.getId(), sendData);
		}
        if (res.getCode() == ResultCodeEnum.OK.getCode()) {
            LOGGER.info("向设备下发指令成功，deviceId={},isBlock ={}, code={},msg={},data = {}"
                , devices.getId(), isBlock, res.getCode(), res.getMsg(), JSONUtil.toJsonStr(sendData));
            return R.ok();
        } else {
            LOGGER.error("向设备下发指令失败，deviceId={}, isBlock ={}, code={},msg={},data = {}"
                , devices.getId(), isBlock, res.getCode(), res.getMsg(), JSONUtil.toJsonStr(sendData));
            return R.failed(res.getMsg());
        }
    }


    /**
     * 异步发送
     *
     * @param deviceId
     * @param sendData
     * @return
     */
    private static ResponseData sentAsyncCommand(int deviceId, SendObjectData sendData) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message = JSONUtil.toJsonStr(sendData);
        return CommandSender.sentAsyncCommand(deviceId, message);
    }


    /**
     * 同步发送
     *
     * @param deviceId
     * @param sendData
     * @param timeout  超时时间，单位毫秒
     * @return
     */
    private static ResponseData sentSyncCommand(int deviceId, SendObjectData sendData, int timeout) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message;
        if (sendData.getTag() == null) {
            long tag = getTag(deviceId, sendData.getRt());
            sendData.setTag(tag);
            message = JSONUtil.toJsonStr(sendData);
            saveMsg(deviceId, tag, rt, message);
        } else {
            message = JSONUtil.toJsonStr(sendData);
        }
        return CommandSender.sentSyncCommand(deviceId, sendData.getTag(), message, timeout);
    }


    public static void reply(byte[] replyData, Integer deviceId) {
        // 回复设备消息
        if (replyData.length > 0 && deviceId != null) {
            // 将字节数组包装成ByteBuf
            ByteBuf byteBuf = Unpooled.buffer(replyData.length);
            byteBuf.writeBytes(replyData);
            ResponseData responseData = CommandSender.sentAsyncCommand(deviceId, byteBuf);
            LOGGER.info("=====>回复设备消息结果 code={}, msg={}", responseData.getCode(), responseData.getMsg());
        }
    }


    /**
     * 同步非阻塞发送
     * 只记录消息日志，不会阻塞等待消息回复，相当于异步，但还是会在消息接收处，将日志状态改成已回复。
     *
     * @param deviceId
     * @param sendData
     * @return
     */
    private static ResponseData sentSyncNonBlockCommand(int deviceId, SendObjectData sendData) {
        Integer rt = sendData.getRt();
        if (rt == null) {
            return ResponseData.getInstance(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getDescription());
        }
        String message;
        if (sendData.getTag() == null) {
            long tag = getTag(deviceId, sendData.getRt());
            sendData.setTag(tag);
            message = JSONUtil.toJsonStr(sendData);
            saveMsg(deviceId, tag, rt, message);
        }else {
            message = JSONUtil.toJsonStr(sendData);
        }
        return CommandSender.sentAsyncCommand(deviceId, message);
    }

}
